fix(login): web login-flow triage 2026-06-12 (F7/F1/F3/F4/F6/F10)#220
Merged
Conversation
…approve icons The arbitrary first-factor / MFA picker offered APPROVE_LOGIN and PASSKEY, which have NO MfaStepRenderer case → selecting one routed into the renderer's "Unknown authentication method: APPROVE_LOGIN" dead-end with a blank icon (F7). These two are device-implicit Layer-1 factors with DEDICATED first-factor entry points (PasskeyLoginButton / ApproveLoginPanel via Layer1Shortcuts) — not generic mid-flow MFA steps. The backend may still list them in availableMethods (e.g. ensureApproveLoginEnrollment auto-enrolls APPROVE_LOGIN), so MethodPickerStep now FILTERS them out of the offered list (NON_RENDERABLE_METHODS) rather than rendering an unusable card. The dedicated shortcuts are untouched. Also add passkey + approve_login icons to StepProgress.helpers METHOD_ICONS so any other place they render shows an icon instead of a blank avatar. Shared code → fixes BOTH app.fivucsas and verify.fivucsas.
…o dead-end)
When a usernameless / cross-device Layer-1 method (QR sign-in, approve-on-another-
device, or passkey-as-first-factor) satisfied the first factor but the tenant flow
needed MORE steps, the server returned an mfaSessionToken WITHOUT final tokens —
but the web dropped it:
- QrLoginPanel set phase='mfa' and rendered only an info alert + back button
(continue suppressed), explicitly DROPPING the poll's mfaSessionToken (in-code
TODO). → F1 ("web does nothing") = F3 ("only a back button").
- ApproveLoginPanel had no multi-step branch at all.
- PasskeyLoginButton handed a tokenless response to onSuccess → "missing access
token" dead-end (the passkey-success-into-MFA case behind F2).
Fix (one mechanism): a shared Layer1Continuation handoff. Each first-factor entry
point now calls onMfaRequired(mfaSessionToken + next-step availableMethods) when
Layer 1 approved but more steps remain; the host enters the EXISTING MethodPicker /
MfaStepRenderer continuation:
- app.fivucsas (LoginPage): handleLayer1MfaRequired sets the session + methods and
shows the picker / single-method dispatcher.
- verify.fivucsas (HostedLoginApp + LoginMfaFlow): new initialContinuation prop
resumes LoginMfaFlow into the picker / first MFA step.
Poll/response types (QrLoginPoll, ApproveLoginPoll) gain availableMethods +
completedMethods. The back-button-only dead-end is removed.
Shared code → fixes BOTH surfaces.
… cluster "Approve on another device" (APPROVE_LOGIN number-matching) sat visually identical to passkey + QR in the no-typing usernameless cluster, but it is genuinely NOT usernameless — it needs an identifier to route the push (backend set supports_usernameless=false in V74), so pressing it then asked for an email, which felt out of place (F4). Layer1Shortcuts now splits the shortcuts into two groups: the truly no-typing methods (passkey + QR) first, then a separate labelled "Approve from another device" section whose button says "…— enter your email", so the later email step is expected. New i18n keys approveLogin.sectionLabel + buttonWithEmail (en + tr). Shared code → fixes BOTH surfaces.
The mfa.nfc.notSupported message said NFC works only on Android Chrome — true for the Web NFC API but incomplete. Reworded (en + tr) to point users to the FIVUCSAS mobile app for managing their NFC card on other devices, or to pick another method. Does NOT claim a native NFC *login* exists (verified: none does — mobile NFC is enrol/eMRTD only). Rendered by NfcStep (shared) → both surfaces.
…tion) useFaceDetection ran synchronous ML inference on EVERY requestAnimationFrame on the main thread (~60fps), producing `[Violation] requestAnimationFrame handler took <N>ms` warnings + visible jank during the face step (F10). Low-risk fix: add a timestamp gate (MIN_DETECT_INTERVAL_MS = 55ms ≈ 18fps) to all three detection loops (FaceLandmarker / BlazeFace / MediaPipe FaceDetector). The rAF cadence (and cleanup/cancellation) is unchanged; inference is just SKIPPED on intervening frames. Detection / centering / captureReady are unaffected. NOT a Worker/OffscreenCanvas rewrite (deliberately out of scope).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the web login-flow bugs from the 2026-06-12 triage (
docs/LOGIN_TRIAGE_2026-06-12.md). All changes are SHARED code → each fix lands on BOTH app.fivucsas (LoginPage/TwoFactorDispatcher) and verify.fivucsas (HostedLoginApp/LoginMfaFlow) at once. Separate, revertible commit per fix. NO hardcoded UI strings — every new string viat()with bothen.json+tr.json.Fixes
F7 — picker offered non-renderable methods (
cc8e0e5)MethodPickerStepofferedAPPROVE_LOGIN/PASSKEY, which have noMfaStepRenderercase → selecting one hit the renderer'sdefault:"Bilinmeyen yöntem: APPROVE_LOGIN" dead-end with a blank icon. These are device-implicit Layer-1 factors with dedicated first-factor entry points (PasskeyLoginButton/ApproveLoginPanelviaLayer1Shortcuts), not generic MFA steps.NON_RENDERABLE_METHODSset; the offered list is filtered (.filter(m => !NON_RENDERABLE_METHODS.has(m.methodType))) BEFORE sort/render, so the card never appears — not just re-worded. The dedicated Layer-1 shortcuts are untouched.passkey+approve_loginicons toStepProgress.helpersMETHOD_ICONSso any other render path shows an icon, not a blank avatar.F1 + F3 (+ passkey-success behind F2) — usernameless Layer-1 → MFA continuation dead-end (
ba71717)On
APPROVED-without-accessToken,QrLoginPanelsetphase='mfa'and rendered only an info alert + back button, dropping the poll'smfaSessionToken(in-code TODO).ApproveLoginPanelhad no multi-step branch;PasskeyLoginButtonhanded a tokenless response toonSuccess→ "missing access token" dead-end.Layer1Continuationtype. Each first-factor entry point now calls a newonMfaRequired(mfaSessionToken + next-step availableMethods + completedMethods + step counters)when Layer 1 approved but more steps remain. Poll types (QrLoginPoll,ApproveLoginPoll) gainedavailableMethods+completedMethods.LoginPage):handleLayer1MfaRequiredsets the session token + methods and enters the EXISTINGMethodPickerStep(or single-methodTwoFactorDispatcher).HostedLoginApp+LoginMfaFlow): newinitialContinuationprop resumesLoginMfaFlowinto its picker / first MFA step.F4 — "approve from another device" mis-grouped (
aa56d38)Layer1Shortcutsnow splits into two groups: truly no-typing (passkey + QR) first, then a separate labelled "Approve from another device" section whose button reads "…— enter your email", so the email step is expected. New keysapproveLogin.sectionLabel+approveLogin.buttonWithEmail(en + tr).F6 — NFC web copy clarification (
5a1b295)Reworded
mfa.nfc.notSupported(en + tr): scanning in the browser is Chrome-on-Android only; on other devices enrol/manage the NFC card in the FIVUCSAS mobile app or pick another method. Does not claim a native NFC login exists (verified: none does).F10 — face-detect rAF
[Violation](2595e4b)useFaceDetectionran ML inference on everyrequestAnimationFrameon the main thread. Added a timestamp gate (MIN_DETECT_INTERVAL_MS = 55ms ≈ 18fps) to all three detection loops — inference is skipped on intervening frames; the rAF cadence/cleanup and detection/captureReadyare unchanged. Not a Worker/OffscreenCanvas rewrite (out of scope).Verification
npm test(vitest): 122 files / 1211 tests green (2 files / 25 tests skipped, pre-existing).SKIP_MODEL_FETCH=1 npm run build: passes (tsc clean + vite build OK). Chunk-size + onnxruntimeevalwarnings are pre-existing dependency-level.Deploy note
Merging auto-deploys app.fivucsas (Hostinger) — which also brings #219 live, resolving the F10 autocomplete deploy-lag. verify.fivucsas (Hetzner container) needs its separate rebuild to pick these up.